/*******************************************************************************
 * Copyright IBM Corp. and others 2017
 *
 * This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License 2.0 which accompanies this
 * distribution and is available at https://www.eclipse.org/legal/epl-2.0/
 * or the Apache License, Version 2.0 which accompanies this distribution
 * and is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License, v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception [1] and GNU General Public
 * License, version 2 with the OpenJDK Assembly Exception [2].
 *
 * [1] https://www.gnu.org/software/classpath/license.html
 * [2] https://openjdk.org/legal/assembly-exception.html
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
 *******************************************************************************/

#ifndef OMR_DATATYPES_INLINES_INCL
#define OMR_DATATYPES_INLINES_INCL

#include "il/OMRDataTypes.hpp"
#include "infra/Assert.hpp"

TR::DataType*
OMR::DataType::self()
   {
   return static_cast<TR::DataType*>(this);
   }

const TR::DataType*
OMR::DataType::self() const
   {
   return static_cast<const TR::DataType*>(this);
   }

OMR::DataType::operator int()
   {
   return self()->getDataType();
   }

bool
OMR::DataType::isInt8()
   {
   return self()->getDataType() == TR::Int8;
   }

bool
OMR::DataType::isInt16()
   {
   return self()->getDataType() == TR::Int16;
   }

bool
OMR::DataType::isInt32()
   {
   return self()->getDataType() == TR::Int32;
   }

bool
OMR::DataType::isInt64()
   {
   return self()->getDataType() == TR::Int64;
   }

bool
OMR::DataType::isIntegral()
   {
   return self()->isInt8() || self()->isInt16() || self()->isInt32() || self()->isInt64();
   }

bool
OMR::DataType::isFloatingPoint()
   {
   return self()->isBFPorHFP();
   }

bool
OMR::DataType::isVector()
   {
   return _type >= TR::FirstVectorType && _type <= TR::LastVectorType;
   }

bool
OMR::DataType::isVectorElement()
   {
   return (_type > TR::NoType) && (_type <= TR::NumVectorElementTypes);
   }

bool
OMR::DataType::isBFPorHFP()
   {
   return self()->getDataType() == TR::Float || self()->getDataType() == TR::Double;
   }

bool
OMR::DataType::isDouble()
   {
   return self()->getDataType() == TR::Double;
   }

bool
OMR::DataType::isFloat()
   {
   return self()->getDataType() == TR::Float;
   }

bool
OMR::DataType::isAddress()
   {
   return self()->getDataType() == TR::Address;
   }

bool
OMR::DataType::isAggregate()
   {
   return self()->getDataType() == TR::Aggregate;
   }

TR::DataType&
OMR::DataType::operator=(const TR::DataType& rhs)
   {
   _type = rhs._type;
   return *(static_cast<TR::DataType *>(this));
   }

TR::DataType&
OMR::DataType::operator=(TR::DataTypes rhs)
   {
   _type = rhs;
   return *(static_cast<TR::DataType *>(this));
   }

bool
OMR::DataType::operator==(const TR::DataType& rhs)
   {
   return _type == rhs._type;
   }

bool
OMR::DataType::operator==(TR::DataTypes rhs)
   {
   return _type == rhs;
   }

bool
OMR::DataType::operator!=(const TR::DataType& rhs)
   {
   return _type != rhs._type;
   }

bool
OMR::DataType::operator!=(TR::DataTypes rhs)
   {
   return _type != rhs;
   }

bool
OMR::DataType::operator<=(const TR::DataType& rhs)
   {
   return _type <= rhs._type;
   }

bool
OMR::DataType::operator<=(TR::DataTypes rhs)
   {
   return _type <= rhs;
   }

bool		
OMR::DataType::operator<(const TR::DataType& rhs)		
   {		
   return _type < rhs._type;		
   }		
		
bool		
OMR::DataType::operator<(TR::DataTypes rhs)		
   {		
   return _type < rhs;		
   }		
		
bool		
OMR::DataType::operator>=(const TR::DataType& rhs)		
   {		
   return _type >= rhs._type;		
   }		
		
bool		
OMR::DataType::operator>=(TR::DataTypes rhs)		
   {		
   return _type >= rhs;		
   }		
		
bool		
OMR::DataType::operator>(const TR::DataType& rhs)		
   {		
   return _type > rhs._type;		
   }		
		
bool		
OMR::DataType::operator>(TR::DataTypes rhs)		
   {		
   return _type > rhs;		
   }		

TR::DataTypes
OMR::DataType::createVectorType(TR::DataType elementType, TR::VectorLength length)
   {
   TR::DataTypes et = elementType.getDataType();

   TR_ASSERT_FATAL(et > TR::NoType && et <= TR::NumVectorElementTypes,
                   "Invalid vector element type %d\n", et);
   TR_ASSERT_FATAL(length > TR::NoVectorLength && length <= TR::NumVectorLengths,
                   "Invalid vector length %d\n", length);

   TR::DataTypes type = static_cast<TR::DataTypes>(TR::FirstVectorType + (length - 1) * TR::NumVectorElementTypes + et - 1);

   return type;
   }

TR::DataType
OMR::DataType::getVectorElementType()
   {
   TR_ASSERT_FATAL(isVector() || isMask(), "getVectorElementType() is called on non-vector and oon non-mask type\n");

   if (isVector())
      return static_cast<TR::DataTypes>((_type - TR::FirstVectorType) % TR::NumVectorElementTypes + 1);
   else
      return static_cast<TR::DataTypes>((_type - TR::FirstMaskType) % TR::NumVectorElementTypes + 1);
   }

TR::VectorLength
OMR::DataType::getVectorLength()
   {
   TR_ASSERT_FATAL(isVector() || isMask(), "getVectorLength() is called on non-vector and non-mask type\n");

   if (isVector())
      return static_cast<TR::VectorLength>((_type - TR::FirstVectorType) / TR::NumVectorElementTypes + 1);
   else
      return static_cast<TR::VectorLength>((_type - TR::FirstMaskType) / TR::NumVectorElementTypes + 1);
   }

TR::VectorLength
OMR::DataType::bitsToVectorLength(int32_t bits)
   {
   TR::VectorLength length = TR::NoVectorLength;
   switch (bits)
      {
      case 64:  length = TR::VectorLength64; break;
      case 128: length = TR::VectorLength128; break;
      case 256: length = TR::VectorLength256; break;
      case 512: length = TR::VectorLength512; break;
      }

   return length;
   }


bool
OMR::DataType::isMask()
   {
   return _type >= TR::FirstMaskType && _type <= TR::LastMaskType;
   }


TR::DataTypes
OMR::DataType::createMaskType(TR::DataType elementType, TR::VectorLength length)
   {
   TR::DataTypes et = elementType.getDataType();

   TR_ASSERT_FATAL(et > TR::NoType && et <= TR::NumVectorElementTypes,
                   "Invalid vector element type %d\n", et);
   TR_ASSERT_FATAL(length > TR::NoVectorLength && length <= TR::NumVectorLengths,
                   "Invalid vector length %d\n", length);

   TR::DataTypes type = static_cast<TR::DataTypes>(TR::FirstMaskType + (length - 1) * TR::NumVectorElementTypes + et - 1);

   return type;
   }


TR::DataTypes
OMR::DataType::vectorFromMaskType(TR::DataType maskType)
   {
   TR_ASSERT_FATAL(maskType.isMask(), "vectorFromMaskType should take mask type\n");

   return static_cast<TR::DataTypes>(maskType - TR::NumVectorTypes);
   }


#endif // OMR_DATATYPES_INLINES_INCL
